package ai.ann.image.filter.filter;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Float;
import java.awt.image.BufferedImage;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Stack;

import ai.ann.image.filter.beans.ColorRangeBean;
import ai.ann.image.filter.beans.ShapeBean;
import ai.ann.image.filter.bound.BoundDescription;
import ai.ann.image.filter.bound.TypeDescriptor;




public class ImageFilter2 {
	private final int BACKGROUND = Color.BLACK.getRGB();
	private LinkedList colorList;
	private int detail;
	
	public ImageFilter2(LinkedList colorList,int detail){
		this.colorList = colorList;
		this.detail = detail;
	}
	
	public BufferedImage geFiltered(BufferedImage image){
		int w = image.getWidth(),h=image.getHeight();
		BufferedImage tmp = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
		Graphics g = tmp.getGraphics();
		Color recognizedColor;
		for(int y=0;y<h;y+=this.detail){
			for(int x=0;x<w;x+=this.detail){
				recognizedColor = recognizeColor(image.getRGB(x,y));
				if(recognizedColor!=null){
					g.setColor(recognizedColor);
					g.fillRect(x,y,this.detail,this.detail);
				}
			}
		}
		return tmp;
	}
	private Color recognizeColor(int rgb){
		Color c1 = new Color(rgb), c2;
		ColorRangeBean tmp;
		int error,red1,red2,green1,green2,blue1,blue2;
		
		for(Iterator i=this.colorList.iterator();i.hasNext();){
			tmp = (ColorRangeBean)i.next();
			c2 = tmp.getColor();
			error = tmp.getError();
			red1 = c1.getRed(); red2 = c2.getRed();
			green1 = c1.getGreen(); green2 = c2.getGreen();
			blue1 = c1.getBlue(); blue2 = c2.getBlue();
			if(
				(red1>=Math.round(red2-error)&&red1<Math.round(red2+error))&&
				(green1>=Math.round(green2-error)&&green1<Math.round(green2+error))&&
				(blue1>=Math.round(blue2-error)&&blue1<Math.round(blue2+error))
			){
				return tmp.getRepresentativeColor();
			}
		}
		return null;
	}
	/**
	 * @param image
	 * @return List of ShapeBean.
	 */
	public LinkedList geShape(BufferedImage image,TypeDescriptor[]typeDescriptors){
		LinkedList ans = new LinkedList();
		Point2D.Float tmpPoint;
		ShapeBean tmpShapeBean; Shape tmpShape;
		int w = image.getWidth(),h=image.getHeight(),c1;
		for (int i = 0; i < typeDescriptors.length; i++) {
			c1 = typeDescriptors[i].getColor().getRGB();
			for(int y=0;y<h;y+=this.detail){
				for(int x=0;x<w;x+=this.detail){
					if(image.getRGB(x,y)==c1){
						tmpPoint = new Point2D.Float(x,y);
						tmpShapeBean = this.getContainerShapeOf(ans,tmpPoint,c1);
						if(tmpShapeBean==null){
							tmpShape = explore(image,c1,tmpPoint);
							if(tmpShape!=null){
								ans.add(new BoundDescription(tmpShape,new TypeDescriptor(new Color(c1),typeDescriptors[i].getType())));
							}
						}else{
							x+=tmpShapeBean.getShape().getBounds().getWidth();
						}
					}
				}
			}
		}
		return ans;
	}
	private ShapeBean getContainerShapeOf(LinkedList shapeList,Point2D.Float point,int color){
		ShapeBean ans = null;
		Shape tmpShape; int tmpColor;
		for(Iterator i=shapeList.iterator();i.hasNext();){
			ans = (ShapeBean)i.next();
			tmpShape = ans.getShape();
			tmpColor = ans.getRepresentativeColor().getRGB();
			if(tmpColor==color && tmpShape.contains(point)){
				return ans;
			}
		}
		return null;
	}
	/*
	 * Para conjuntos independientes:
	 */
	private Shape explore(BufferedImage image, int target, Point2D.Float p){
		Stack stack = new Stack();
		GeneralPath shape = new GeneralPath();
		int w=image.getWidth(),h=image.getHeight(),x,y,z,
		
		h_y=0,//High y.
		h_x=0,//High x.
		l_y=h,//Low y.
		l_x=w;//Low x.
		
		boolean [][] flag = new boolean [h][w];
		boolean existABound = false;
		for(int i=0;i<flag.length;i++){
			for(int j=0;j<flag[i].length;j++){
				flag[i][j]=false;
			}
		}
		x = (int) p.getX();
		y = (int) p.getY();
		if((y>=0&&y<h) && (x>=0&&x<w) && !flag[y][x] && image.getRGB(x,y)==target){
			h_y=((int)p.getY());//High y.
			h_x=((int)p.getX());//High x.
			l_y=((int)p.getY());//Low y.
			l_x=((int)p.getX());//Low x.
			stack.push(p);
			shape.moveTo(x,y);
		}
		while(!stack.empty()){
			p = (Float)stack.pop();
			x = ((int)p.getX());
			y = ((int)p.getY());
			
			h_y=(y>h_y)?y:h_y;
			h_x=(x>h_x)?x:h_x;
			l_y=(y<l_y)?y:l_y;
			l_x=(x<l_x)?x:l_x;
			
			existABound = flag[y][x] = true;
			
			z = y-1;
			if((z>=0&&z<h) && !flag[z][x]){
				if(image.getRGB(x,z)==target){
					stack.push(new Point2D.Float(x,z));
				}else shape.lineTo(x,z);
			}
			
			z = x+1;
			if((z>=0&&z<w) &&!flag[y][z]){
				if(image.getRGB(z,y)==target){
					stack.push(new Point2D.Float(z,y));
				}else shape.lineTo(z,y);
			}
			
			z = y+1;
			if((z>=0&&z<h) &&!flag[z][x]){
				if(image.getRGB(x,z)==target){
					stack.push(new Point2D.Float(x,z));
				}else shape.lineTo(x,z);
			}
			
			z = x-1;
			if((z>=0&&z<w) &&!flag[y][z]){
				if(image.getRGB(z,y)==target){
					stack.push(new Point2D.Float(z,y));
				}else shape.lineTo(z,y);
			}
		}
		shape.closePath();
		return shape;
	}
}
